import time
import coin.exchange.base.kr_rest.public_client_base as pubcb
import coin.exchange.huobi.kr_rest.native_public_client as npubc
from coin.exchange.huobi.kr_rest.product import HuobiProduct
from coin.exchange.huobi.kr_rest.constants import active_symbol_list
from coin.exchange.base.kr_rest.rest_client_base import RestUpdate

from coin.proto.coin_market_query_pb2 import (
    ProductTicker,
    ExchangeTicker,
    ProductKlineElement,
    ProductKline,
)


class HuobiFeedParser(object):
  @staticmethod
  def parse_native_ticker(ticker, product):
    assert ticker['status'] == "ok"
    return ProductTicker(last=float(ticker['tick']['close']),
                         high=float(ticker['tick']['high']),
                         low=float(ticker['tick']['low']),
                         ask=float(ticker['tick']['ask'][0]),
                         ask_size=float(ticker['tick']['ask'][1]),
                         bid=float(ticker['tick']['bid'][0]),
                         bid_size=float(ticker['tick']['bid'][1]),
                         exchange_timestamp=int(float(ticker['ts']) * 1e+6),
                         native_symbol=product.native_symbol,
                         symbol=product.symbol)

  @staticmethod
  def parse_exchange_ticker(update_msg):
    product_ticker_list = []
    for ticker in update_msg:
      try:
        assert ticker['status'] == "ok"
        symbol = ticker['ch'].split('.')[1]
        product = HuobiProduct.FromStr(symbol)
        product_ticker = ProductTicker(last=float(ticker['tick']['close']),
                                       high=float(ticker['tick']['high']),
                                       low=float(ticker['tick']['low']),
                                       ask=float(ticker['tick']['ask'][0]),
                                       ask_size=float(ticker['tick']['ask'][1]),
                                       bid=float(ticker['tick']['bid'][0]),
                                       bid_size=float(ticker['tick']['bid'][1]),
                                       exchange_timestamp=int(float(ticker['ts']) * 1e+6),
                                       native_symbol=product.native_symbol,
                                       symbol=product.symbol)
        product_ticker_list.append(product_ticker)
      except Exception as e:
        print(ticker, type(e), e)
        continue
    assert len(product_ticker_list) > 0
    return ExchangeTicker(exchange="Huobi", each_ticker=product_ticker_list)

  @staticmethod
  def parse_native_kline(update_msg, product, kline_period, start_time, end_time):
    """Sample kline response:
      {
        "status": "ok",
        "ch": "market.btcusdt.kline.1day",
        "ts": 1499223904680,
        "data": [
      {
          "id": 1499184000,
          "amount": 37593.0266,
          "count": 0,
          "open": 1935.2000,
          "close": 1879.0000,
          "low": 1856.0000,
          "high": 1940.0000,
          "vol": 71031537.97866500
        },
    """
    klines = update_msg['data']
    kline_group = []
    for kline in klines:
      kline_timestamp = kline['id']
      if kline_timestamp >= start_time.timestamp() and \
         kline_timestamp < end_time.timestamp():
        product_kline = ProductKlineElement(kline_timestamp=int(kline_timestamp * 1e+9),
                                            open=float(kline['open']),
                                            high=float(kline['high']),
                                            low=float(kline['low']),
                                            close=float(kline['close']),
                                            volume=float(kline['amount']),
                                            turnover=float(kline['vol']))
        kline_group.append(product_kline)
    assert len(kline_group) > 0
    return ProductKline(
        symbol=product.symbol,
        native_symbol=product.native_symbol,
        exchange='Huobi',
        market_type='Spot',
        kline_period=kline_period,
        klines=kline_group,
    )


class HuobiPublicClient(pubcb.PublicClientBase):
  ProductType = HuobiProduct

  def __init__(self):
    self.npubc = npubc.HuobiNativePublicClient()

  def query_ticker_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_ticker(symbol)
    update.msg = HuobiFeedParser.parse_native_ticker(update.msg, product)
    return update

  def query_exchange_ticker_impl(self):
    reqtime = time.time()
    ticker_list = []
    # Since there are too many symbols, and we only trade some of the.
    # Limit the querying to the smaller symbol set.
    for symbol in active_symbol_list:
      ticker_list.append(self.npubc.get_ticker(symbol).msg)
    restime = time.time()
    update = RestUpdate(reqtime, restime, ticker_list)
    update.msg = HuobiFeedParser.parse_exchange_ticker(update.msg)
    return update

  def query_history_kline_impl(self, product, kline_period, start_time, end_time):
    size = 2000
    native_symbol = product.native_symbol
    update = self.npubc.get_history_kline(native_symbol, kline_period, size)
    update.msg = HuobiFeedParser.parse_native_kline(update.msg,
                                                    product,
                                                    kline_period,
                                                    start_time,
                                                    end_time)
    return update

  def query_level_book_impl(self, product):
    raise NotImplementedError("Huobi query level book Not Implemented")
